#pragma once

#include "db_Menage.hpp"
#include "httplib.h"
#include "daemon.hpp"
#include <iostream>
#include <functional>

// 网络通信服务器模块
namespace vod
{
#define WWW_ROOT "./www"        // 静态资源根目录 -- wb根目录
#define VIDEO_ROOT "/video/"    // Video Path -- 视频路径, 第一个/前面会自动添加WWW_ROOT
#define IMAGE_ROOT "/image/"    // Image Path -- 图片路径, 第一个/前面会自动添加WWW_ROOT

    TableVideo *ptv = nullptr;  // 数据库管理模块类指针

    class Server
    {
    public:
        Server(uint16_t port, std::string newPwd);
        bool RunServer();       // 运行server

    private:
        static void Insert(const httplib::Request &req, httplib::Response &resp);    // 增加
        static void Delete(const httplib::Request &req, httplib::Response &resp);    // 删除
        static void Update(const httplib::Request &req, httplib::Response &resp);    // 更新
        static void SelectOne(const httplib::Request &req, httplib::Response &resp); // 查询单个
        static void SelectAll(const httplib::Request &req, httplib::Response &resp); // 查询全部 | 模糊查询

    private:
        uint16_t port_;
        std::string newPwd_;
        httplib::Server svr_;
    };

    //------------------------------------- Constructor or Starting module API -----------------------------------

    Server::Server(uint16_t port, std::string newPwd = "") : port_(port), newPwd_(newPwd)
    {
    }

    bool Server::RunServer()
    {
        // 0.set daemon
        daemon(newPwd_);
        // 1.init TableVideo
        ptv = new TableVideo();

        // 2.Create Directory
        FileUtil fu;
        if (fu.CreateDirectory(WWW_ROOT) == false) return false;    // ./www
        std::string VideoPath = WWW_ROOT; 
        VideoPath += VIDEO_ROOT;
        if (fu.CreateDirectory(VideoPath) == false) return false;   // ./www/video/
        std::string ImagePath = WWW_ROOT;
        ImagePath += IMAGE_ROOT;
        if (fu.CreateDirectory(ImagePath) == false) return false;   // ./www/image/

        // 3.Building an HTTP server
        // 3.1、Set static resource root directory
        if (svr_.set_mount_point("/", WWW_ROOT) == false) return false;
        
        // 3.2、Set the mapping relationship between requests and request processing methods
        svr_.Post("/video", Insert);            // 上传视频处理方法       
        svr_.Delete("/video/(\\d+)", Delete);   // 删除视频处理方法
        svr_.Put("/video/(\\d+)", Update);      // 修改视频处理方法
        svr_.Get("/video/(\\d+)", SelectOne);   // 查询单个视频处理方法
        svr_.Get("/video", SelectAll);          // 查询全部视频 | 模糊查询视频 处理方法

        // 3.3、Start server
        svr_.listen("0.0.0.0", port_);

        return true;
    }

    //------------------------------------ Request processing function -------------------------------------------
    void Server::Insert(const httplib::Request &req, httplib::Response &resp)
    {
        // 1.判断字段名是否存在
        // MultipartFormData = {name(字段名), content_type(正文类型), filename(文件名), content(文件里面的数据)}
        if (req.has_file("name") == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"上传的信息格式错误!!!"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        httplib::MultipartFormData name = req.get_file_value("name");   // video "name"
        if (req.has_file("info") == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"上传的信息格式错误!!!"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        httplib::MultipartFormData info = req.get_file_value("info");   // video "info"
        if (req.has_file("video") == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"上传的信息格式错误!!!"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        httplib::MultipartFormData video = req.get_file_value("video"); // video "storage Data"
        if (req.has_file("image") == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"上传的信息格式错误!!!"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        httplib::MultipartFormData image = req.get_file_value("image"); // video "Cover Data"

        // 2.通过文件工具类把视频和视频封面数据写入到本地指定路径进行存储
        std::string video_data = name.content;
        std::string info_data = info.content;
        // ./www/video/"name + filename"
        std::string video_path = WWW_ROOT + std::string(VIDEO_ROOT) + video_data + video.filename; // add video_name 避免文件名冲突
        std::string image_path = WWW_ROOT + std::string(IMAGE_ROOT) + info_data + image.filename; // add video_name 避免文件名冲突

        // MultipartFormData = {name(字段名), content_type(正文类型), filename(文件名), content(文件里面的数据)}
        FileUtil fu1(video_path);
        if (fu1.WriteFileData(video.content) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"视频文件存储失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        FileUtil fu2(image_path);
        if (fu2.WriteFileData(image.content) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"视频封面存储失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }

        // 3.更新数据库 -- insert new table data
        Json::Value root;
        root["name"] = video_data;
        root["info"] = info_data;
        // 插入相对路径 -- 去除静态wb根目录
        video_path.erase(0, sizeof(WWW_ROOT) - 1);
        root["video"] = video_path;
        image_path.erase(0, sizeof(WWW_ROOT) - 1);
        root["image"] = image_path;
        if (ptv->Insert(root) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"数据库插入新表数据失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        resp.set_redirect("/index.html", 303); // 4.请求处理成功后客户端重新请求网页主页信息
    }

    void Server::Delete(const httplib::Request &req, httplib::Response &resp)
    {
        // 1.获取视频id
        int video_id = std::stoi(req.matches[1]);
        // 2.删除本地存储的视频和视频封面
        Json::Value video_data;
        if (ptv->SelectOne(video_id, &video_data) == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"没有查询到需要删除的视频"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }

        // 与Insert的path对应 -- WWW_ROOT + databse table's data path
        std::string video_path = WWW_ROOT + video_data["video"].asString();
        std::string image_path = WWW_ROOT + video_data["image"].asString();

        if (remove(video_path.c_str()) < 0)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"删除视频相关信息失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        if (remove(image_path.c_str()) < 0)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"删除视频相关信息失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        // 3.删除数据库中的表数据
        if (ptv->Delete(video_id) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"数据库删除新表数据失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
    }

    void Server::Update(const httplib::Request &req, httplib::Response &resp)
    {
        // 1.获取视频id
        int video_id = std::stoi(req.matches[1]);
        // 2.获取请求正文 -- 请求正文格式: json -- 反序列化正文
        Json::Value root;
        if (JsonUtil::Deserialize(req.body, &root) == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"请求格式不正确"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        // 4.修改数据库表数据
        if (ptv->Update(video_id, root) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"修改数据库表数据失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
    }

    void Server::SelectOne(const httplib::Request &req, httplib::Response &resp)
    {
        // 1.获取视频id
        int video_id = std::stoi(req.matches[1]);
        // 2.在数据库通过id查询视频信息
        Json::Value video_data;
        if (ptv->SelectOne(video_id, &video_data) == false)
        {
            resp.status = 400;
            resp.body = R"({"result":false, "reason":"没有查询到指定的视频"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        // 3.构建响应报文 -- json格式
        if (JsonUtil::Serialize(video_data, &resp.body) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"序列化响应正文失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        resp.set_header("Content-Type", "application/json");
    }

    void Server::SelectAll(const httplib::Request &req, httplib::Response &resp)
    {
        // SelectAll：/video -- SelectLike: /vide?search="key" -> key是查询关键字
        // 1.判断是否有search -> yes -> 获取key
        Json::Value videos;
        if (req.has_param("search"))
        {
            std::string key = req.get_param_value("search");
            // 2. 通过模糊查询获取视频信息
            if (ptv->SelectLike(key, &videos) == false)
            {
                resp.status = 400;
                resp.body = R"({"result":false, "reason":"没有查询到指定的视频"})";
                resp.set_header("Content-Type", "application/json");
                return;
            }
        }
        else
        {
            if (ptv->SelectAll(&videos) == false) // 查询全部视频信息
            {
                resp.status = 400;
                resp.body = R"({"result":false, "reason":"没有查询到指定的视频"})";
                resp.set_header("Content-Type", "application/json");
                return;
            }
        }
        // 3.构建响应正文 -- json格式
        if (JsonUtil::Serialize(videos, &resp.body) == false)
        {
            resp.status = 500;
            resp.body = R"({"result":false, "reason":"序列化响应正文失败"})";
            resp.set_header("Content-Type", "application/json");
            return;
        }
        resp.set_header("Content-Type", "application/json");
    }
}